/**
	*****************************************************************************
	* @file     cmem7_efuse.c
	*
	* @brief    CMEM7 EFUSE source file
	*
	*
	* @version  V1.0
	* @date     3. September 2013
	*
	* @note               
	*           
	*****************************************************************************
	* @attention
	*
	* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
	* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
	* TIME. AS A RESULT, CAPITAL-MICRO SHALL NOT BE HELD LIABLE FOR ANY DIRECT, 
	* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
	* FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
	* CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
	*
	* <h2><center>&copy; COPYRIGHT 2013 Capital-micro </center></h2>
	*****************************************************************************
	*/
	
#include "cmem7_efuse.h"
#include "cmem7_rtc.h"

typedef void (*WAIT)(void);
static WAIT wait;

static uint8_t efuse_Crc8Bit(uint8_t data, uint8_t crc) {
	uint8_t newCrc;
	uint8_t d[8], c[8], i;
	
	for (i = 0; i < 8; i++) {
		d[i] = (data >> i) & 0x01;
		c[i] = (crc >> i) & 0x01;
	}
	
	newCrc = d[7] ^ d[6] ^ d[0] ^ c[0] ^ c[6] ^ c[7];
	newCrc |= (d[6] ^ d[1] ^ d[0] ^ c[0] ^ c[1] ^ c[6]) << 1;
	newCrc |= (d[6] ^ d[2] ^ d[1] ^ d[0] ^ c[0] ^ c[1] ^ c[2] ^ c[6]) << 2;
	newCrc |= (d[7] ^ d[3] ^ d[2] ^ d[1] ^ c[1] ^ c[2] ^ c[3] ^ c[7]) << 3;
	newCrc |= (d[4] ^ d[3] ^ d[2] ^ c[2] ^ c[3] ^ c[4]) << 4;
	newCrc |= (d[5] ^ d[4] ^ d[3] ^ c[3] ^ c[4] ^ c[5]) << 5;
	newCrc |= (d[6] ^ d[5] ^ d[4] ^ c[4] ^ c[5] ^ c[6]) << 6;
	newCrc |= (d[7] ^ d[6] ^ d[5] ^ c[5] ^ c[6] ^ c[7]) << 1;
	
	return newCrc;
}

static uint8_t efuse_Crc(EFUSE_AesKey* key, uint8_t lock) {
	uint8_t crc = 0;
	
	crc = efuse_Crc8Bit(key->key0, crc);
	crc = efuse_Crc8Bit(key->key0 >> 8, crc);
	crc = efuse_Crc8Bit(key->key0 >> 16, crc);
	crc = efuse_Crc8Bit(key->key0 >> 24, crc);
	crc = efuse_Crc8Bit(key->key1, crc);
	crc = efuse_Crc8Bit(key->key1 >> 8, crc);
	crc = efuse_Crc8Bit(key->key1 >> 16, crc);
	crc = efuse_Crc8Bit(key->key1 >> 24, crc);
	crc = efuse_Crc8Bit(key->key2, crc);
	crc = efuse_Crc8Bit(key->key2 >> 8, crc);
	crc = efuse_Crc8Bit(key->key2 >> 16, crc);
	crc = efuse_Crc8Bit(key->key2 >> 24, crc);
	crc = efuse_Crc8Bit(key->key3, crc);
	crc = efuse_Crc8Bit(key->key3 >> 8, crc);
	crc = efuse_Crc8Bit(key->key3 >> 16, crc);
	crc = efuse_Crc8Bit(key->key3 >> 24, crc);
	crc = efuse_Crc8Bit(key->key0, crc);
	crc = efuse_Crc8Bit(key->key0 >> 8, crc);
	crc = efuse_Crc8Bit(key->key0 >> 16, crc);
	crc = efuse_Crc8Bit(lock, crc);
	crc = efuse_Crc8Bit(0x0, crc);
	crc = efuse_Crc8Bit(0x0, crc);
	
	return crc;
}


void EFUSE_Init(EFUSE_InitTypeDef* init) {
	assert_param(init);
	assert_param(IS_EFUSE_TMRF(init->EFUSE_TMRF));
	
	EFUSE->USER_CTRL_b.TMRF = init->EFUSE_TMRF;
	if (init->timing) {
		EFUSE->TIMING_0_b.TPWPH = 
		  (init->timing->EFUSE_Tpwph * (SYSTEM_CLOCK_FREQ / 1000)) >> 2;
		EFUSE->TIMING_1_b.TPWPH = 
		  (init->timing->EFUSE_Tpwph * (SYSTEM_CLOCK_FREQ / 1000)) & 0x00000003;
		EFUSE->TIMING_0_b.TRAC = init->timing->EFUSE_Trac;
		EFUSE->TIMING_0_b.TRAH = init->timing->EFUSE_Trah;
		EFUSE->TIMING_0_b.TRPW = init->timing->EFUSE_Trpw;
		EFUSE->TIMING_0_b.TRC = init->timing->EFUSE_Trc;
		EFUSE->TIMING_0_b.TESR = init->timing->EFUSE_Tesr;
		EFUSE->TIMING_0_b.TPRS = init->timing->EFUSE_Tprs;
		EFUSE->TIMING_1_b.TPIT = init->timing->EFUSE_Tpi;
		EFUSE->TIMING_1_b.TPP = init->timing->EFUSE_Tpp;
		EFUSE->TIMING_1_b.TEPS = init->timing->EFUSE_Teps;
		EFUSE->TIMING_1_b.TPWPS = init->timing->EFUSE_Tpwps;
		wait = init->EFUSE_Wait;
	}
}

/* It only can be written once */
BOOL EFUSE_Write(EFUSE_AesKey* key) {
  assert_param(key);	
	
	if (EFUSE->USER_CTRL_b.LOCK || EFUSE->USER_CTRL_b.BUSY) {
		return FALSE;
	}
	
	EFUSE->USER_CTRL_b.WR_EN = TRUE;
	EFUSE->USER_DATA0 = key->key0;
	EFUSE->USER_DATA1 = key->key1;
	EFUSE->USER_DATA2 = key->key2;
	EFUSE->USER_DATA3 = key->key3;
	EFUSE->USER_DATA4_b.CRC = efuse_Crc(key, 0x1);
	EFUSE->USER_DATA4_b.LOCK = 1;
	
	RTC_DelayMs(1);
	while (EFUSE->USER_CTRL_b.BUSY) {
		if (wait) {
			(*wait)();
		}
	}
	
	return EFUSE->USER_CTRL_b.WR_CRC_ERR ? FALSE : TRUE;
}

BOOL EFUSE_Compare(EFUSE_AesKey* key) {
  assert_param(key);	
	
	if (EFUSE->USER_CTRL_b.BUSY) {
		return FALSE;
	}
	
	EFUSE->USER_CTRL_b.RD_EN = TRUE;
	EFUSE->USER_DATA0 = key->key0;
	EFUSE->USER_DATA1 = key->key1;
	EFUSE->USER_DATA2 = key->key2;
	EFUSE->USER_DATA3 = key->key3;
	
	RTC_DelayMs(1);
	while (EFUSE->USER_CTRL_b.BUSY) {
		if (wait) {
			(*wait)();
		}
	}
	
	return EFUSE->USER_CTRL_b.COMPARE_FAIL ? FALSE : TRUE;	
}

